﻿using System;
using System.Linq;

namespace Borg.Framework
{
    public class PlugComboBox : ComboBox, IAttachable
    {
        private void AddItem(PlugTypeInfo plugTypeInfo)
        {
            if (plugTypeInfo.GetParamValue("OrderIndex") == null)
                Items.Add(plugTypeInfo, plugTypeInfo.ExtensionTypeInfo);
            else
            {
                var key = (from kvPair in Items
                              let paramValue = ((PlugTypeInfo) kvPair.Key).GetParamValue("OrderIndex")
                              where (paramValue != null) &&
                                (float) paramValue >= (float) plugTypeInfo.GetParamValue("OrderIndex")
                              select kvPair.Key).FirstOrDefault();

                if (key == null)
                    Items.Add(plugTypeInfo, plugTypeInfo.ExtensionTypeInfo);
                else
                {
                    var index = Items.IndexOf(key);
                    Items.Insert(index, plugTypeInfo, plugTypeInfo.ExtensionTypeInfo);
                }
            }
        }

        public void Attach(SlotInfo slot)
        {
            Slot = slot;

            Slot.Registered += Registered;
            Slot.Deregistered += Deregistered;
            Slot.SelectionChanged += SelectionChanged;

            if (Runtime.IsCompositionAllowed)
                Slot.SelectionMode = SelectionMode.Single;

            Items.Clear();

            foreach (var plugType in Slot.RegisteredPlugTypeInfos)
                AddItem(plugType);

            Enabled = true;
        }

        public void Deregistered(object sender, RegisterEventArgs args)
        {
            Items.Remove(args.PlugTypeInfo);
        }

        public void Detach()
        {
            Enabled = false;
            Items.Clear();
            
            Slot.Registered -= Registered;
            Slot.Deregistered -= Deregistered;
            Slot.SelectionChanged -= SelectionChanged;
            
            Slot = null;
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);

            if (Slot != null)
                Detach();
        }

        protected override void OnSelectedIndexChanged(EventArgs e)
        {
            base.OnSelectedIndexChanged(e);

            if (noUpdate || SelectedItem.Key == null)
                return;

            noUpdate = true;
            var key = (PlugTypeInfo)SelectedItem.Key;

            if (Slot.PluggedPlugInfos.Count > 0)
                Slot.PluggedPlugInfos[0].Unplug(Slot);

            var plug = key.CreateExtension(Slot);

            plug.Plug(Slot);
            Slot.SelectedPlug = plug;
            noUpdate = false;
        }

        public void Registered(object sender, RegisterEventArgs args)
        {
            AddItem(args.PlugTypeInfo);
        }

        private void SelectionChanged(object sender, SelectionChangedEventArgs args)
        {
            if (noUpdate)
                return;

            noUpdate = true;

            if (args.AddedPlugs.Count == 0)
                SelectedIndex = -1;
            else
                SelectedKey = args.AddedPlugs[0].PlugTypeInfo;

            noUpdate = false;
        }

        public SlotInfo Slot { get; private set; }
    }
}

